﻿/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.SQLite;
using System.Data.Common;
using System.Diagnostics;
using System.IO;

namespace IvyOrm.Test
{
    [TestClass]
    [DeploymentItem("x86")] //SQLite
    public class ValueQueryExtensionTest
    {
        #region ValueQuery
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_NoResults_returns_empty_array()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");

                var results = connection.ValueQuery<string>("select ColumnA from SingleColumnTable order by ColumnA").ToArray();

                Assert.AreEqual(0, results.Length);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_MultiValues()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");

                var results = connection.ValueQuery<string>("select ColumnA from SingleColumnTable order by ColumnA").ToArray();

                Assert.AreEqual("alpha", results[0]);
                Assert.AreEqual("beta", results[1]);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_NullValue()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", (object)null);

                var results = connection.ValueQuery<string>("select ColumnA from SingleColumnTable order by ColumnA").ToArray();

                Assert.IsNull(results[0]);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_MultipleColumns_Returns_First_Column()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table MultiColumnTable (ColumnA TEXT, ColumnB TEXT)");
                connection.ExecuteNonQuery("insert into MultiColumnTable (ColumnA) values (@0)", "alpha");

                var results = connection.ValueQuery<string>("select ColumnA, ColumnB from MultiColumnTable order by ColumnA").ToArray();

                Assert.AreEqual("alpha", results[0]);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_NullArguments_Throws_Exception()
        {
            using (var connection = new SQLiteConnection())
            {
                AssertB.ThrowsException<ArgumentNullException>(() => ValueQueryExtension.ValueQuery<string>((DbConnection)null, new IvyOrmOptions(), ""));
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_InvalidConverter_Throws_Exception()
        {
            var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;");
            connection.Open();
            connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");

            ValueConverterFactory nullValueConverter = CompilerService.CreateInstanceFromCode(@"
                                    public class NullValueConverter : ValueConverterFactory
                                    {
                                        public override Converter<object, object> GetConverter(Type sourceType, Type destinationType)
                                        {
                                            return null;
                                        }
                                    }");


            AssertB.ThrowsException<NotSupportedException>(() => ValueQueryExtension.ValueQuery<string>(connection, new IvyOrmOptions { ValueConverterFactory = nullValueConverter }, "select ColumnA from SingleColumnTable order by ColumnA"));
        }


        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_Transaction()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");
                using (var transaction = connection.BeginTransaction())
                {
                    var results = transaction.ValueQuery<string>("select ColumnA from SingleColumnTable order by ColumnA").ToArray();

                    Assert.AreEqual("alpha", results[0]);
                    Assert.AreEqual("beta", results[1]);
                }
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_Transaction_NullArguments()
        {
            DbTransaction nullTransaction = null;

            DbTransaction badTransaction = new BadDbTransaction();

            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                using (var transaction = connection.BeginTransaction())
                {
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueQuery<string>(nullTransaction, new IvyOrmOptions(), ""));
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueQuery<string>(badTransaction, new IvyOrmOptions(), ""));
                }
            }
        }

        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueQuery_Logging()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                string lastLog = null;
                var options = new IvyOrmOptions();
                options.LoggingFunction = (l, s, e) => lastLog = s;

                var result = connection.ValueQuery<string>(options, "select 'alpha'");
                Assert.AreEqual("alpha", result[0]);

                Assert.IsTrue(lastLog != null && lastLog.ToUpperInvariant().Contains("select 'alpha'".ToUpperInvariant()));
            }
        }
        #endregion

        #region ValueSingle
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_NullArguments_Throws_Exception()
        {
            using (var connection = new SQLiteConnection())
            {
                AssertB.ThrowsException<ArgumentNullException>(() => ValueQueryExtension.ValueSingle<string>((DbConnection)null, new IvyOrmOptions(), ""));
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_NoResults_Throws_Exception()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");

                AssertB.ThrowsException(() =>
                    connection.ValueSingle<string>("select ColumnA from SingleColumnTable order by ColumnA")
                    );
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_MultiValues_Returns_First_Result()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");

                string results = connection.ValueSingle<string>("select ColumnA from SingleColumnTable order by ColumnA");

                Assert.AreEqual("alpha", results);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_Transaction()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");
                using (var transaction = connection.BeginTransaction())
                {
                    var results = transaction.ValueSingle<string>("select ColumnA from SingleColumnTable order by ColumnA");

                    Assert.AreEqual("alpha", results);
                }
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_Transaction_NullArguments()
        {
            DbTransaction nullTransaction = null;
            DbTransaction badTransaction = new BadDbTransaction();

            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                using (var transaction = connection.BeginTransaction())
                {
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueSingle<string>(nullTransaction, new IvyOrmOptions(), ""));
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueSingle<string>(badTransaction, new IvyOrmOptions(), ""));

                }
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingle_Logging()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                string lastLog = null;
                var options = new IvyOrmOptions();
                options.LoggingFunction = (l, s, e) => lastLog = s;

                var result = connection.ValueSingle<string>(options, "select 'alpha'");
                Assert.AreEqual("alpha", result);

                Assert.IsTrue(lastLog != null && lastLog.ToUpperInvariant().Contains("select 'alpha'".ToUpperInvariant()));
            }
        }
        #endregion

        #region ValueSingleOrDefault
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleOrDefault_NullArguments_Throws_Exception()
        {
            using (var connection = new SQLiteConnection())
            {
                AssertB.ThrowsException<ArgumentNullException>(() => ValueQueryExtension.ValueSingleOrDefault<string>((DbConnection)null, new IvyOrmOptions(), ""));
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleOrDefault_NoResults_Returns_Default()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");

                var results = connection.ValueSingleOrDefault<string>("select ColumnA from SingleColumnTable order by ColumnA");

                Assert.AreEqual(null, results);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleOrDefault_MultiValues_Returns_First_Result()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");

                string results = connection.ValueSingleOrDefault<string>("select ColumnA from SingleColumnTable order by ColumnA");

                Assert.AreEqual("alpha", results);
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleOrDefault_Transaction()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();
                connection.ExecuteNonQuery("create table SingleColumnTable (ColumnA TEXT)");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "alpha");
                connection.ExecuteNonQuery("insert into SingleColumnTable (ColumnA) values (@0)", "beta");
                using (var transaction = connection.BeginTransaction())
                {
                    var results = transaction.ValueSingleOrDefault<string>("select ColumnA from SingleColumnTable order by ColumnA");
                    Assert.AreEqual("alpha", results);

                    results = transaction.ValueSingleOrDefault<string>("select ColumnA from SingleColumnTable where ColumnA=@0", "none");
                    Assert.AreEqual(null, results);
                }
            }
        }
        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleOrDefault_Transaction_NullArguments()
        {
            DbTransaction nullTransaction = null;

            DbTransaction badTransaction = new BadDbTransaction();

            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                using (var transaction = connection.BeginTransaction())
                {
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueSingleOrDefault<string>(nullTransaction, new IvyOrmOptions(), ""));
                    AssertB.ThrowsException<ArgumentException>(() => ValueQueryExtension.ValueSingleOrDefault<string>(badTransaction, new IvyOrmOptions(), ""));
                }
            }
        }

        [TestMethod]
        [TestCategory("SQLite")]
        public void ValueSingleeOrDefault_Logging()
        {
            using (var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"))
            {
                connection.Open();

                string lastLog = null;
                var options = new IvyOrmOptions();
                options.LoggingFunction = (l, s, e) => lastLog = s;

                var result = connection.ValueSingleOrDefault<string>(options, "select 'alpha'");
                Assert.AreEqual("alpha", result);

                Assert.IsTrue(lastLog != null && lastLog.ToUpperInvariant().Contains("select 'alpha'".ToUpperInvariant()));
            }
        }
        #endregion
    }
}
